import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

public class TicketRequestHelper {

	public final static String LOGIN_URL = "https://dynamic.12306.cn/otsweb/loginAction.do?method=login";

	public final static String LOGIN_RAND_CODE_IMG_URL = "https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=lrand&";

	public final static String ORDER_RAND_CODE_IMG_URL = "https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=randp&";

	public final static String TRAIN_QUERY_URL = "https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=queryLeftTicket&orderRequest.train_no=&trainPassType=QB&trainClass=QB%23D%23Z%23T%23K%23QT%23&includeStudent=00&seatTypeAndNum=&orderRequest.start_time_str=00%3A00--24%3A00";

	public final static String ORDER_SUBMIT_URL = "https://dynamic.12306.cn/otsweb/order/querySingleAction.do?method=submutOrderRequest";

	public final static String ORDER_COMFIRM_SUBMIT_URL = "https://dynamic.12306.cn/otsweb/order/confirmPassengerAction.do?method=confirmPassengerInfoSingle";

	private TicketRequestHelper() {
		super();
		System.setProperty("javax.net.ssl.trustStore", "otweb.jks");
	}

	private static TicketRequestHelper instance = new TicketRequestHelper();

	private Map<String, String> cookieMap = new HashMap<String, String>();

	public Map<String, String> getCookieMap() {
		return cookieMap;
	}

	public void setCookieMap(Map<String, String> cookieMap) {
		this.cookieMap = cookieMap;
	}

	public static TicketRequestHelper getInstance() {
		return instance;
	}

	public byte[] getLoginRandCodeAsBytes() throws IOException {
		if (cookieMap == null || cookieMap.isEmpty()) {
			cookieMap = getConnection(LOGIN_URL).execute().cookies();
		}
		String url = LOGIN_RAND_CODE_IMG_URL + "0.0"
				+ System.currentTimeMillis();
		return getAsBytes(url, cookieMap);
	}

	public byte[] getOrderRandCodeAsBytes() throws IOException {
		String url = ORDER_RAND_CODE_IMG_URL + "0.0"
				+ System.currentTimeMillis();
		return getAsBytes(url, cookieMap);
	}

	public LoginResult login(String loginName, String loginPwd, String randCode) {
		LoginResult result = new LoginResult();
		try {
			Map<String, String> data = new HashMap<String, String>();
			data.put("loginUser.user_name", loginName);
			data.put("user.password", loginPwd);
			data.put("randCode", randCode);
			Document doc = post(LOGIN_URL, data);
			if (null == doc) {
				result.successed = false;
				result.error = "网络连接慢或者无法连接";
				return result;
			}
			String docStr = doc.toString();
			if (docStr.contains("我的订单")) {
				result.successed = true;
				return result;
			}
			if (docStr.contains("系统维护中")) {
				result.successed = false;
				result.error = "系统维护中";
				return result;
			}
			String scriptError = StringUtils.substringBetween(docStr,
					"var message = \"", "\";");
			if (StringUtils.isNotBlank(scriptError)) {
				result.successed = false;
				result.error = scriptError;
				return result;
			}
			String randErr = null;
			try {
				randErr = doc.select("#randErr").first().child(1).text();
				if (StringUtils.isNotBlank(randErr)) {
					result.successed = false;
					result.error = randErr;
					return result;
				}
			} catch (Exception e) {
				System.out.println(doc);
			}
			try {
				String err_bot = doc.select("#err_bot").text();
				if (StringUtils.isNotBlank(err_bot)) {
					result.successed = false;
					result.error = err_bot;
					return result;
				}
			} catch (Exception e) {
				System.out.println(doc);
			}
			result.successed = false;
			return result;
		} catch (Exception e) {
			e.printStackTrace();
		}
		result.successed = false;
		return result;
	}

	public OrderResult order(Train t, String seatType, String randCode,
			List<User> users) {
		OrderResult result = new OrderResult();
		if (StringUtils.isBlank(t.getYpInfoDetail())) {
			result.successed = false;
			result.error = "您选择的列车已经没有任何票剩下来了";
			return result;
		}
		try {
			// 预定页面
			Document doc = post(ORDER_SUBMIT_URL, t.toQueryMap());
			if (null == doc) {
				result.successed = false;
				result.error = "网站长时间无响应";
				return result;
			}
			String errMsg = getErrorMsg(doc);
			if (errMsg != null) {
				result.successed = false;
				result.error = errMsg;
				return result;
			}

			String htmlToken;
			try {
				htmlToken = doc.select("#confirmPassenger input").first().val();
			} catch (Exception e) {
				System.out.println(doc);
				result.successed = false;
				result.error = e.getMessage();
				return result;
			}

			// 确认提交订单
			Map<String, String> map = t.toOrderMap();
			Connection conn = getConnection(ORDER_COMFIRM_SUBMIT_URL);
			for (String entry : map.keySet()) {
				conn.data(entry, map.get(entry));
			}
			conn.data("org.apache.struts.taglib.html.TOKEN", htmlToken);
			conn.data("randCode", randCode);
			conn.data("checkbox0", "0");
			conn.data("checkbox1", "1");
			conn.data("checkbox9", "Y");
			conn.data("checkbox9", "Y");
			conn.data("checkbox9", "Y");
			conn.data("checkbox9", "Y");
			conn.data("checkbox9", "Y");

			for (int i = 1; i <= users.size(); i++) {
				User user = users.get(i - 1);
				conn.data("oldPassengers",
						user.getName() + ",1," + user.getId());
				conn.data("passengerTickets", seatType + ",1," + user.getName()
						+ ",1," + user.getId() + "," + user.getMobile() + ",Y");
				conn.data("passenger_" + i + "_cardno", user.getId());
				conn.data("passenger_" + i + "_cardtype", "1");
				conn.data("passenger_" + i + "_mobileno", user.getMobile());
				conn.data("passenger_" + i + "_name", user.getName());
				conn.data("passenger_" + i + "_seat", seatType);
				conn.data("passenger_" + i + "_ticket", "1");
			}
			doc = post(conn);
			if (null == doc) {
				result.successed = false;
				result.error = "网站长时间无响应";
				return result;
			}
			String docStr = doc.toString();

			if (docStr.contains("订单信息")) {
				result.successed = true;
				return result;
			} else {
				errMsg = getErrorMsg(doc);
				if (errMsg != null) {
					result.successed = false;
					result.error = errMsg;
					return result;
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
			result.successed = false;
			result.error = e.getMessage();
			return result;
		}
		result.successed = false;
		return result;
	}

	private String getErrorMsg(Document doc) {
		try {
			if (doc.toString().contains("系统维护中")) {
				return "系统维护中";
			}

			String s = doc.select("script").last().data().trim();
			s = IOUtils.readLines(new StringReader(s)).get(0).trim();
			s = s.replace("var message = \"", "").replace("\";", "");
			if (s.length() > 0) {
				return s;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Document post(String url, Map<String, String> data) {
		try {
			return getConnection(url).data(data).post();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public Document post(Connection conn) {
		try {
			return conn.post();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public Document get(String url) {
		try {
			return getConnection(url).get();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private byte[] getAsBytes(String url, Map<String, String> cookieMap) {
		try {
			byte[] imageData = getConnection(url).execute().bodyAsBytes();
			return imageData;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public Connection getConnection(String url) {
		Connection conn = Jsoup.connect(url);
		conn.userAgent("Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)");
		conn.header("Accept-Encoding", "");
		// conn.ignoreHttpErrors(true);
		conn.timeout(60000);
		if (cookieMap != null) {
			for (Entry<String, String> entry : cookieMap.entrySet()) {
				conn.cookie(entry.getKey(), entry.getValue());
			}
		}
		return conn;
	}

	public QueryResult query(String fromStation, String toStation, String date) {
		QueryResult result = new QueryResult();
		String from = StationInfoHelper.getStationCode(fromStation);
		String to = StationInfoHelper.getStationCode(toStation);
		try {
			StringBuilder sb = new StringBuilder();
			sb.append(TRAIN_QUERY_URL);
			sb.append("&orderRequest.train_date=").append(date);
			sb.append("&orderRequest.from_station_telecode=").append(from);
			sb.append("&orderRequest.to_station_telecode=").append(to);

			Document doc = get(sb.toString());
			if (null == doc) {
				result.successed = false;
				result.error = "网站没有响应";
				return result;
			}
			String leftSeatStr = doc.text()
					.replace(Jsoup.parse("&nbsp;").text(), "")
					.replace('\\', ' ');

			List<Train> list = new ArrayList<Train>();
			if (leftSeatStr.contains("系统维护中")) {
				result.successed = false;
				result.error = "系统维护中";
				return result;
			} else if ("-10".equals(leftSeatStr)) {
				result.successed = false;
				result.error = "登陆信息已过期，需要再次登陆";
				return result;
			} else {
				System.out.println(leftSeatStr);
				Elements inputs = doc.select("input");
				String[] lines = leftSeatStr.split("n");
				for (int i = 0, len = lines.length; i < len; i++) {
					String[] fields = lines[i].trim().split(",");
					System.out.println(lines[i]);
					Train t = new Train();
					t.setStationTrainCode(fields[1]);

					String onclick = inputs.get(i).attr("onclick");
					if (StringUtils.isNotBlank(onclick)) {
						String[] selectStr_arr = onclick
								.replace("javascript:getSelected('", "")
								.replace("')", "").split("#");

						t.setTrainNo(selectStr_arr[3]);
						t.setFromStationName(selectStr_arr[7]);
						t.setFromStationTelecode(selectStr_arr[4]);
						t.setFromStationTelecodeName(selectStr_arr[7]);
						t.setToStationName(selectStr_arr[8]);
						t.setToStationTelecode(selectStr_arr[5]);
						t.setToStationTelecodeName(selectStr_arr[8]);
						t.setTrainStartTime(selectStr_arr[2]);
						t.setArriveTime(selectStr_arr[6]);
						t.setLishi(selectStr_arr[1]);
						t.setYpInfoDetail(selectStr_arr[9]);
					} else {
						String[] fromStationInfo = StringUtils.split(fields[2]);
						t.setFromStationName(trimSpace(fromStationInfo[0]));
						t.setTrainStartTime(trimSpace(fromStationInfo[1]));
						String[] toStationInfo = StringUtils.split(fields[3]);
						t.setToStationName(trimSpace(toStationInfo[0]));
						t.setArriveTime(trimSpace(toStationInfo[1]));
					}
					String[] leftSeat = new String[10];
					for (int j = 0; j < leftSeat.length; j++) {
						leftSeat[j] = fields[j + 5];
					}
					t.setLeftSeat(leftSeat);
					t.setDate(date);
					list.add(t);
				}
			}
			result.successed = true;
			result.trains = list;
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			result.successed = false;
			result.error = "查询出现异常:" + e;
			return result;
		}
	}

	private String trimSpace(String s) {
		String temp = s.replaceAll("(^[ |　|\\s]*|[ |　|\\s]*$)", "");
		temp = temp.replaceAll("　", "");
		return temp;
	}
}

final class LoginResult {
	public String error;
	public boolean successed;
	public boolean randCodeRefreshed;
}

final class QueryResult {
	public String error;
	public boolean successed;
	public List<Train> trains;
}

final class OrderResult {
	public String error;
	public boolean successed;
	public boolean randCodeRefreshed;
}
